#include "autodue.h"

int main(int argc, char* argv[]) {

	int i = 0;

	//----------INIZIO PARSING DATABASE----------//

	// Come unico parametro il programma prende il file xml
	// contenente la configurazione degli esperimenti
	if (argc <= 1)
	{
		// Blocco di istruzioni da visualizzare in caso di errata invocazione del programma
		printf("Usage: %s docname\n", argv[0]);
		return(EXIT_SUCCESS);
	}
	else
	{
		// Qui viene salvato il percorso del file dati in input e salvato il contenuto
		// in una struttura dati apposita
		filename_xml = argv[1];
		doc_xml = getdoc((char *)filename_xml);
	}
	printf("Autodue: lettura della configurazione dal file %s:\n\n", filename_xml);

	// Questa variabile indica il numero di esecuzioni contemporanee dello script che si desidera lanciare:
	// se non e' indicato nel file xml sara' 1
	int nthreads;
	char *nthreads_string = (char *)getfirstvalue(doc_xml, (xmlChar *)XPATH_NTHREADS);
	if (nthreads_string)
	{
		nthreads = atoi(nthreads_string);
	}
	else
	{
		nthreads = 1;
	}
	printf("Esecuzioni parallele:\t\t%d\n", nthreads);

	// Queste variabili conterranno il numero di processi generati in esecuzione
	// e lo stato di uscita di un processo figlio
	int *nchildren, status;

	// Viene inizializzata la memoria condivisa tra i vari threads
	// per memorizzare il numero di processi figli
	int shm_id_nch;

	shm_id_nch = shmget((key_t)1234, sizeof(int), 0666 | IPC_CREAT);
	if (shm_id_nch < 0)
	{
		fprintf(stderr, ERROR_STRING, "Impossibile creare memoria condivisa");
		exit(EXIT_FAILURE);
	}
	int *shm_addr_nch = (int *)shmat(shm_id_nch, (void *)0, 0);
	if (shm_addr_nch == (int *)-1)
	{
		fprintf(stderr, ERROR_STRING, "Indirizzo di memoria condivisa non valido");
		exit(EXIT_FAILURE);
	}
	nchildren = shm_addr_nch;

	*nchildren = 0;

	// Viene inizializzato il semaforo che gestirà la concorrenza per la scrittura sul file di log
	sem_t * sem_log;

	int shm_id_sem;

	shm_id_sem = shmget((key_t)1000, sizeof(sem_t), 0666 | IPC_CREAT);
	if (shm_id_sem < 0)
	{
		fprintf(stderr, ERROR_STRING, "Impossibile creare memoria condivisa");
		exit(EXIT_FAILURE);
	}
	sem_t *shm_addr_sem = (sem_t *)shmat(shm_id_sem, (void *)0, 0);
	if (shm_addr_sem == (sem_t *)-1)
	{
		fprintf(stderr, ERROR_STRING, "Indirizzo di memoria condivisa non valido");
		exit(EXIT_FAILURE);
	}
	sem_log = shm_addr_sem;

	sem_init(sem_log, 1, 1);

	// Viene salvato il formato dei file immagine con cui rappresentare i grafi
	const char* img_ext = (char *)getfirstvalue(doc_xml, (xmlChar *)XPATH_IMG_EXT);
	if (img_ext)
	{
		printf("Formato immagini:\t\t%s\n\n", img_ext);
	}
	else
	{
		printf("Non saranno generate immagini\n\n");
	}

	// Stringhe per contenere il path del file .g e le righe lette dai files
	char fileg_path[BUFF_SIZE], buffer[BUFF_SIZE], filename_log[BUFF_SIZE];
	// Stringa necessaria per la composizione degli xpath
	xmlChar xml_string[BUFF_SIZE];

	// Viene interrogato il file xml alla ricerca della cartella di installazione di subdue
	const char *subdue_bin_path = (char *)getfirstvalue(doc_xml, (xmlChar *)XPATH_SD_BP);
	if (subdue_bin_path)
	{
		printf("Subdue bin path:\t\t%s\n", subdue_bin_path);
	}
	else
	{
		fprintf(stderr, ERROR_STRING, "Impossibile trovare la directory di installazione di subdue");
		exit(EXIT_FAILURE);
	}

	// Viene aperto il file in cui scrivere i dati estratti dal database
	// in una sintassi comprensibile da subdue (dopo aver creato l'albero delle directory
	// se se il prefix èp stato correttamente specificato nel file xml)
	const char *prefix = (char *)getfirstvalue(doc_xml, (xmlChar *)XPATH_PREFIX);
	if (prefix)
	{
		printf("Directory degli esperimenti:\t%s\n", prefix);
		sprintf(buffer, "mkdir -p %s", prefix);
		system(buffer);
	}
	else
	{
		fprintf(stderr, ERROR_STRING, "Non è stata specificata la cartella radice degli esperimenti");
		exit(EXIT_FAILURE);
	}

	// Se non è stata specificata alcuna sezione per estrarre i dati nel file sql
	// si presuppone che il file .g sia stato stato generato in precedenza e sia pronto per l'uso
	int needsql = tagcount(doc_xml, (xmlChar *)XPATH_SQL);

	// Se correttamente specificato nela file xml viene creato il file .g col nome desiderato,
	// altrimenti viene utilizzato il valore di default "graph.g"
	const char *filename_g = (char *)getfirstvalue(doc_xml, (xmlChar *)XPATH_FILE_G);
	if (filename_g)
	{
		printf("File dei grafi:\t\t\t%s\n", filename_g);
	}
	else if (needsql)
	{
		filename_g = "graph.g";
		printf("File dei grafi:\t\t\t(default)\n");
		// Viene pulita la cartella che sarà usata per gli esperimenti
		sprintf(buffer, "rm -Rf %s/*", prefix);
		system(buffer);
	}
	else
	{
		fprintf(stderr, ERROR_STRING, "Non è stata specificato ne' un file contenente grafi ne' un server sql dal quale generarlo");
		exit(EXIT_FAILURE);
	}

	// Viene controllato che il file .g esista se non generato dal database
	sprintf(fileg_path, "%s/%s", prefix, filename_g);
	if(!needsql)
	{
		FILE* file_g;
		if ((file_g = fopen(fileg_path, "r")))
		{
			fclose(file_g);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il file .g");
			exit(EXIT_FAILURE);
		}
	}

	// Altrimenti vengono cercati i parametri per la connessione al database da cui estrarre i dati
	if (needsql)
	{
		// Variabili utili ad ottenere i risultati delle query sul database MySQL
		MYSQL *conn;
		MYSQL_RES *res_nodes, *res_edges, *res_graphid;
		MYSQL_ROW row;

		// Viene interrogato il file xml alla ricerca dell'indirizzo dell'host
		// su cui sta girando il server MySQL
		const char *host = (char *)getfirstvalue(doc_xml, (xmlChar*)XPATH_HOST);
		if (host)
		{
			printf("Indirizzo server SQL:\t\t%s\n", host);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare l'indirizzo del server SQL");
			exit(EXIT_FAILURE);
		}

		// Viene interrogato il file xml alla ricerca della porta dell'host
		// su cui sta girando il server MySQL
		const char *port = (char *)getfirstvalue(doc_xml, (xmlChar*)XPATH_PORT);
		if (port)
		{
			printf("Porta server SQL:\t\t%s\n", port);
		}
		else
		{
			printf("Porta server SQL:\t\t(default)\n");
		}

		// Viene interrogato il file xml alla ricerca dell' username da utilizzare
		// per autenticarsi sul server MySQL
		const char *username = (char *)getfirstvalue(doc_xml, (xmlChar*)XPATH_USERNAME);
		if (username)
		{
			printf("Nome utente:\t\t\t%s\n", username);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare l'username per il login sul server SQL");
			exit(EXIT_FAILURE);
		}

		// Viene interrogato il file xml alla ricerca della password da utilizzare
		// per autenticarsi sul server MySQL
		const char *password = (char *)getfirstvalue(doc_xml, (xmlChar*)XPATH_PASSWORD);
		if (password)
		{
			printf("Password:\t\t\t");
			i = 0;
			while (i++ < strlen(password))
				printf("*");
			printf("\n");
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare la password per il login sul server SQL");
			exit(EXIT_FAILURE);
		}

		// Se tutti i parametri per la connessione sono corretti, viene aperto il file
		// in cui scrivere i dati che saranno estratti
		FILE* graph_g;
		if (!(graph_g = fopen(fileg_path, "w+")))
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il file .g");
			exit(EXIT_FAILURE);
		}

		// Stringhe per contenere il le query da eseguire
		char nodes_query_string[BUFF_SIZE], edges_query_string[BUFF_SIZE], query_string[BUFF_SIZE], graphid_query_string[BUFF_SIZE];

		// Viene cercato il numero di database (identificati da una tag <db>)
		// che si vogliono ispezionare alla ricerca di grafi
		int ndb = tagcount(doc_xml, (xmlChar *)XPATH_DB);

		int current_db = 1;

		while (current_db < ndb+1)
		{
			// Viene interrogato il file xml alla ricerca del nome del database corrente
			// da utilizzare per ottenere dati dal server MySQL
			sprintf ((char *)xml_string, XPATH_DBNAME, current_db);
			const char *database = (char *)getfirstvalue(doc_xml, xml_string);
			if (database)
			{
				printf("\nDatabase %d:\t\t\t%s\n", current_db, database);
			}
			else
			{
				fprintf(stderr, ERROR_STRING, "Impossibile trovare il nome del database da utilizzare");
				exit(EXIT_FAILURE);
			}

			// Viene inizializzata una connessione con il server MySQL coi dati specificati sopra
			conn = mysql_init(NULL);
			if (!mysql_real_connect(conn, host, username, password, database, 0, NULL, 0))
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				exit(EXIT_FAILURE);
			}

			// Viene cercato il numero di grafi (identificati da una tag <graph>)
			// che si vogliono estrarre dal database corrente
			sprintf((char *)xml_string, XPATH_GRAPH, current_db);
			int ngraph = tagcount(doc_xml, xml_string);

			int current_graph = 1;

			// Vengono estratti i grafi ad uno ad uno da ugnuno dei db per scriverli nel file .g
			while (current_graph < ngraph+1)
			{
				printf("  Grafo %d\n", current_graph);

				// Viene interrogato il file xml alla ricerca della tabella dei nodi
				// del grafo corrente
				sprintf((char *)xml_string, XPATH_NODES_TABLE, current_db, current_graph);
				const char *nodestable = (char *)getfirstvalue(doc_xml, (xmlChar*)xml_string);
				if (nodestable)
				{
					printf("    Tabella dei nodi:\t\t%s\n", nodestable);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare la tabella dei nodi");
					exit(EXIT_FAILURE);
				}

				// Questa stringa viene usata come cursore per creare la query per
				// estrarre il contenuto della tabella dei nodi con gli attributi specificati
				char* temp = nodes_query_string;

				sprintf((char *)xml_string, XPATH_NODES_ID, current_db, current_graph);
				char *nodes_id = (char *)getfirstvalue(doc_xml, xml_string);
				if (nodes_id)
				{
					printf("      Campo ID nodo:\t\t%s\n", nodes_id);
					temp += sprintf(temp, "select %s", nodes_id);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'id' della tabella dei nodi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_NODES_LBL, current_db, current_graph);
				char *nodes_lbl = (char *)getfirstvalue(doc_xml, xml_string);
				if (nodes_lbl)
				{
					printf("      Campo etichetta:\t\t%s\n", nodes_lbl);
					temp += sprintf(temp, ", %s", getfirstvalue(doc_xml, xml_string));
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'label' della tabella dei nodi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_GRAPH_ATTRIBUTES, current_db, current_graph);
				int nattr = tagcount(doc_xml, xml_string), i = 0;
				xmlChar *attributes[nattr];

				// Qui vengono estratti i vari attributi specificati
				while (i < nattr)
				{
					attributes[i] = getonevalue(doc_xml, xml_string, i);
					printf("      Attributo %d:\t\t%s\n", i+1, attributes[i]);
					temp += sprintf(temp, ", %s", attributes[i]);
					i++;
				}

				sprintf((char *)xml_string, XPATH_NODES_GRAPHID, current_db, current_graph);
				char *nodes_graphid = (char *)getfirstvalue(doc_xml, xml_string);
				if (nodes_graphid)
				{
					printf("      Campo id grafo:\t\t%s\n", nodes_graphid);
					temp += sprintf(temp, ", %s from %s", nodes_graphid, nodestable);
				}
				else
				{
					printf("      Campo id grafo:\t\t(default, assicurarsi di avere un solo grafo nella tabella)\n");
					nodes_graphid = "dummy";
					temp += sprintf(temp, ", (select 1) as %s from %s", nodes_graphid, nodestable);
				}

				// Viene interrogato il file xml alla ricerca della tabella degli archi
				// del grafo corrente
				sprintf((char *)xml_string, XPATH_EDGES_TABLE, current_db, current_graph);
				const char *edgestable = (char *)getfirstvalue(doc_xml, (xmlChar*)xml_string);
				if (edgestable)
				{
					printf("    Tabella degli archi:\t%s\n", edgestable);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare la tabella degli archi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_EDGES_SRC, current_db, current_graph);
				const char *edges_src = (char *)getfirstvalue(doc_xml, xml_string);
				if (edges_src)
				{
					printf("      Campo nodo sorg.:\t\t%s\n", edges_src);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'sourcenode' della tabella degli archi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_EDGES_DEST, current_db, current_graph);
				const char *edges_dest = (char *)getfirstvalue(doc_xml, xml_string);
				if (edges_dest)
				{
					printf("      Campo nodo dest.:\t\t%s\n", edges_dest);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'destnode' della tabella degli archi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_EDGES_LBL, current_db, current_graph);
				const char *edges_lbl = (char *)getfirstvalue(doc_xml, xml_string);
				if (edges_lbl)
				{
					printf("      Campo etichetta:\t\t%s\n", edges_lbl);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'label' della tabella degli archi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_EDGES_DIR, current_db, current_graph);
				const char *edges_dir = (char *)getfirstvalue(doc_xml, xml_string);
				if (edges_dir)
				{
					printf("      Campo direzione arco:\t%s\n", edges_dir);
				}
				else
				{
					fprintf(stderr, ERROR_STRING, "Impossibile trovare il campo 'direction' della tabella degli archi");
					exit(EXIT_FAILURE);
				}

				sprintf((char *)xml_string, XPATH_EDGES_GRAPHID, current_db, current_graph);
				char *edges_graphid = (char *)getfirstvalue(doc_xml, (xmlChar *)xml_string);
				if (edges_graphid)
				{
					printf("      Campo id grafo:\t\t%s\n", edges_graphid);
					sprintf(edges_query_string, "select %s, %s, %s, %s, %s from %s", edges_src, edges_dest, edges_lbl, edges_dir, edges_graphid, edgestable);
				}
				else
				{
					edges_graphid = "dummy";
					printf("      Campo id grafo:\t\t(default)\n");
					sprintf(edges_query_string, "select %s, %s, %s, %s, 1 as %s from %s", edges_src, edges_dest, edges_lbl, edges_dir, edges_graphid, edgestable);
				}


				// Vengono salvati i valori distinti di graphid trovati nella tabella dei nodi
				// Eventuali valori diversi nella tabella di nodi sono da considerarsi un errore sui dati
				sprintf(graphid_query_string, "select distinct(%s) from %s", nodes_graphid, nodestable);
				int ngraphid = 1;
				if (mysql_query(conn, graphid_query_string))
				{
					res_graphid = NULL;
				}
				else
				{
					res_graphid = mysql_store_result(conn);
					ngraphid = (int)res_graphid->row_count;
				}
				int graphid[ngraphid], current_graphid;
				if (res_graphid)
				{
					for (current_graphid = 0; current_graphid < ngraphid; current_graphid++)
					{
						row = mysql_fetch_row(res_graphid);
						graphid[current_graphid] = atoi(row[0]);
					}
					mysql_free_result(res_graphid);
				}
				else
				{
					graphid[0] = 1;
				}

				current_graphid = 0;
				// Viene scritto il grafo corrente nel file .g
				// secondo la sintassi comprensibile da subdue
				while (current_graphid < ngraphid)
				{
					// Si vuole estrarre soltanto i nodi con il valore di graphid corrente
					// Questo segmento verra' sovrascritto ogni volta
					// La query per l'estrazione dei nodi viene effettivamente inviata al server SQL tramite
					// una rinominazione del risultato della prima select per prevedere l'assenza del campo graphid
					sprintf(query_string, "select * from (%s) as temp where temp.%s = %d", nodes_query_string, nodes_graphid, graphid[current_graphid]);
					if (mysql_query(conn, query_string))
					{
						fprintf(stderr, "%s\n", mysql_error(conn));
						return(EXIT_FAILURE);
					}

					// Viene salvato il risultato e contati i nodi per conoscere da quale indice
					// far partire la numerazione degli attributi trasformati in nodi
					res_nodes = mysql_store_result(conn);
					my_ulonglong current_attr = res_nodes->row_count;

					fputs("XP\n", graph_g);

					while ((row = mysql_fetch_row(res_nodes)) != NULL)
					{
						sprintf(buffer, "v %s %s\n", row[0], row[1]);
						fputs(buffer, graph_g);
						i = 0;
						while(i<nattr)
						{
							if (row[2+i])
							{
								sprintf(buffer, "v %llu %s\n", ++current_attr, row[2+i]);
								fputs(buffer, graph_g);
								sprintf(buffer, "u %s %llu %s\n", row[0], current_attr, attributes[i]);
								fputs(buffer, graph_g);
							}
							i++;
						}
					}
					mysql_free_result(res_nodes);

					fputs("\n", graph_g);

					// La query sulla degli tabella degli archi viene effettivamente inviata al server SQL tramite
					// una rinominazione del risultato della prima select per prevedere l'assenza del campo graphid
					sprintf(query_string, "select * from (%s) as temp where temp.%s = %d", edges_query_string, edges_graphid, graphid[current_graphid]);
					if (mysql_query(conn, query_string))
					{
						fprintf(stderr, "%s\n", mysql_error(conn));
						return(EXIT_FAILURE);
					}

					res_edges = mysql_use_result(conn);

					while ((row = mysql_fetch_row(res_edges)) != NULL)
					{
						sprintf(buffer, "%s %s %s %s\n", row[3], row[0], row[1], row[2]);
						fputs(buffer, graph_g);
					}
					mysql_free_result(res_edges);

					if (++current_graphid < ngraphid)
						fputs("\n", graph_g);
				}
				if (current_graph++ < ngraph)
					fputs("\n", graph_g);
			}
			if (current_db++ < ndb)
				fputs("\n", graph_g);

			mysql_close(conn);
		}

		fclose(graph_g);
	}

	//----------FINE PARSING DATABASE----------//

	// Questa variabile conterra' le righe da scrivere nel file di autodue.log, potenzialmente piu' lunghe di BUFF_SIZE
	char log_buffer[LOG_SIZE];

	// Viene inizializzato il file di log con l'intestazione corretta
	sprintf(filename_log, "%s/autodue.log", prefix);
	FILE *autodue_log;
	if (!(autodue_log = fopen(filename_log, "w+")))
	{
		fprintf(stderr, ERROR_STRING, "Impossibile creare il file autodue.log");
		exit(EXIT_FAILURE);
	}
	else
	{
		sprintf(log_buffer, "ExpDir,ReqIter,MaxIter,Beam,Cov,Div,Rep\n");
		fputs(log_buffer, autodue_log);
		fclose(autodue_log);
	}

	//----------INIZIO CONFIGURAZIONE ESPERIMENTI----------//

	// Questa variabile conterra' il numero di esperimenti indicati nel file xml
	int nexp = tagcount(doc_xml, (xmlChar *)XPATH_EXP), current_exp = 0, current_dir = 1;

	char beam_xpath[BUFF_SIZE], iterations_xpath[BUFF_SIZE];

	// Per ogni esperimento vengono estratti dal file xml tutti i valori di beam e iterations indicati
	// e vengono lanciate le esecuzioni dello script autodue.sh con tutte le possibili coppie di tali valori.
	// Lo script eseguira' subdue con i parametri specificati e, se richiesto, le funzioni di
	// calcolo di copertura, diversita' e rappresentativita'
	while(current_exp++ < nexp)
	{
		printf("\nEsperimento %d:\n", current_exp);

		char *sstart = NULL, *sstop = NULL, *sstep = NULL;
		int start = 0, stop = 0, step = 0;

		// Vengono letti e memorizzati i valori di iterations che saranno utilizzati negli esperimenti
		sprintf(iterations_xpath, XPATH_ITERATIONS, current_exp);
		sprintf((char *)xml_string,"%s/value", iterations_xpath);

		// La variabile niterations indichera' il numero totale di valori di iterations specificati nel
		// file xml mentre la variabile nitervalues conterra' solamente il numero di valori di iterations
		// specificati singolarmente nell'xml
		int nitervalues = tagcount(doc_xml, xml_string), niterations = nitervalues;

		// Controlla che effettivamente vi sia un range di iterazioni da leggere
		sprintf((char *)xml_string,"%s/range", iterations_xpath);
		if ((char *)getfirstvalue(doc_xml, (xmlChar *)xml_string))
		{
			// La variabile niterations viene incrementata per ogni valore di iterations contenuto in un intervallo,
			// se specificato nel file xml
			sprintf((char *)xml_string,"%s/range/start", iterations_xpath);
			sstart = (char *)getfirstvalue(doc_xml, (xmlChar *)xml_string);

			sprintf((char *)xml_string,"%s/range/stop", iterations_xpath);
			sstop = (char *)getfirstvalue(doc_xml, (xmlChar *)xml_string);

			sprintf((char *)xml_string,"%s/range/step", iterations_xpath);
			sstep = (char *)getfirstvalue(doc_xml, (xmlChar *)xml_string);

			// Se presenti vengono abilitati anche i valori di iterations di un dato intervallo
			// specificato nel file xml
			if(sstart && sstop && sstep)
			{
				start = atoi((char *)sstart);
				stop = atoi((char *)sstop);
				step = atoi((char *)sstep);

				i = start;
				while (i <= stop)
				{
					niterations++;
					i += step;
				}
			}
			else
			{
				fprintf(stderr, ERROR_STRING, "Iterations: range di valori definito in modo errato");
				exit(EXIT_FAILURE);
			}
		}

		// Questo vettore conterra' tutti i valori di iterations indicati
		int iterations[(niterations)?niterations:1];

		// Se non e' stato specificato alcun valore sara' usasto quello di default di subdue (1)
		if (niterations)
		{
			sprintf((char *)xml_string,"%s/value", iterations_xpath);

			i = 0;

			// Vengono estratti i valori di iterations indicati singolarmente nel file xml
			while (i < nitervalues)
			{
				iterations[i] = atoi((char *)getonevalue(doc_xml, xml_string, i));
				i++;
			}

			// Vengono estratti i valori di iterations contenuti in un intervallo, se specificato nel file xml
			if(sstart || sstop || sstep)
			{
				while (i <= niterations)
				{
					iterations[i] = start;
					start += step;
					i++;
				}
			}
		}
		else
		{
			iterations[0] = 1;
			niterations = 1;

		}

		// Vengono letti e memorizzati i valori di beam che saranno utilizzati negli esperimenti
		sprintf(beam_xpath, XPATH_BEAM, current_exp);

		// La variabile nbeams indichera' il numero totale di valori di iterations specificati nel
		// file xml mentre la variabile nitervalues conterra' solamente il numero di valori di iterations
		// specificati singolarmente nell'xml
		sprintf((char *)xml_string,"%s/value", beam_xpath);
		int nbeamvalues = tagcount(doc_xml, xml_string), nbeams = nbeamvalues;

		i = 0;

		// Controlla che effettivamente vi sia un range di beam da leggere
		sprintf((char *)xml_string,"%s/range", beam_xpath);
		if ((char *)getfirstvalue(doc_xml, xml_string))
		{

			sprintf((char *)xml_string,"%s/range/start", beam_xpath);
			sstart = (char *)getfirstvalue(doc_xml, xml_string);

			sprintf((char *)xml_string,"%s/range/stop", beam_xpath);
			sstop = (char *)getfirstvalue(doc_xml, xml_string);

			sprintf((char *)xml_string,"%s/range/step", beam_xpath);
			sstep = (char *)getfirstvalue(doc_xml, xml_string);

			// Se presenti vengono abilitati anche i valori di beam di un dato intervallo
			// specificato nel file xml
			if(sstart && sstop && sstep)
			{
				start = atoi((char *)sstart);
				stop = atoi((char *)sstop);
				step = atoi((char *)sstep);

				i = start;
				while (i <= stop)
				{
					nbeams++;
					i += step;
				}
			}
			else
			{
				fprintf(stderr, ERROR_STRING, "Beam: range di valori definito in modo errato");
				exit(EXIT_FAILURE);
			}
		}

		// Questo vettore conterra' tutti i valori di beam indicati
		int beams[(nbeams)?nbeams:1];

		// Se non e' stato specificato alcun valore sara' usasto quello di default di subdue (4)
		if (nbeams)
		{
			sprintf((char *)xml_string,"%s/value", beam_xpath);
			i = 0;

			// Vengono estratti i valori di iterations indicati singolarmente nel file xml
			while (i < nbeamvalues)
			{
				beams[i] = atoi((char *)getonevalue(doc_xml, xml_string, i));
				i++;
			}

			// Vengono estratti i valori di iterations contenuti in un intervallo, se specificato nel file xml
			if(sstart || sstop || sstep)
			{
				while (i <= nbeams)
				{
					beams[i] = start;
					start += step;
					i++;
				}
			}
		}
		else
		{
			nbeams = 1;
			beams[0] = 4;
		}


		//----------FINE CONFIGURAZIONE ESPERIMENTI----------//



		//----------INIZIO ESECUZIONE ESPERIMENTI----------//

		// Vengono stampati i valori di iterations che saranno utilizzati.
		i = 0;
		printf("  Iterations:\t\t\t");
		while (i < niterations)
		{
			printf("%d ", iterations[i]);
			i++;
		}
		printf("\n");

		// Vengono stampati i valori di beam che saranno utilizzati.
		i = 0;
		printf("  Beam:\t\t\t\t");
		while(i < nbeams)
		{
			printf("%d ", beams[i]);
			i++;
		}
		printf("\n");

		int current_beam, current_iter;

		// Altri parametri definiti dall'utente
		sprintf((char *)xml_string, XPATH_PARAMETERS, current_exp);
		char *parameters = (char *)getfirstvalue(doc_xml, xml_string);
		if (parameters)
		{
			printf("  Altri parametri:\t\t%s\n", parameters);
		}
		else
		{
			parameters = "";
		}

		// Vengono stampati i parametri dell'evaluator
		sprintf((char *)xml_string, XPATH_COV, current_exp);
		const char* coverage = (char *)getfirstvalue(doc_xml, xml_string);
		if (coverage)
		{
			printf("  Copertura:\t\t\t%s\n", coverage);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il parametro 'coverage'");
			exit(EXIT_FAILURE);
		}
		sprintf((char *)xml_string, XPATH_DIV, current_exp);
		const char* diversity = (char *)getfirstvalue(doc_xml, xml_string);
		if (diversity)
		{
			printf("  Diversita':\t\t\t%s\n", diversity);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il parametro 'diversity'");
			exit(EXIT_FAILURE);
		}
		sprintf((char *)xml_string, XPATH_REP, current_exp);
		const char* representativeness = (char *)getfirstvalue(doc_xml, xml_string);
		if (representativeness)
		{
			printf("  Rappresentativita':\t\t%s\n", representativeness);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il parametro 'representativeness'");
			exit(EXIT_FAILURE);
		}
		sprintf((char *)xml_string, XPATH_VER, current_exp);
		const char* verbose = (char *)getfirstvalue(doc_xml, xml_string);
		if (verbose)
		{
			printf("  Verbose:\t\t\t%s\n", verbose);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il parametro 'verbose'");
			exit(EXIT_FAILURE);
		}
		sprintf((char *)xml_string, XPATH_KEEP, current_exp);
		const char* keep_files = (char *)getfirstvalue(doc_xml, xml_string);
		if (keep_files)
		{
			printf("  Mantieni files temporanei:\t%s\n\n", keep_files);
		}
		else
		{
			fprintf(stderr, ERROR_STRING, "Impossibile trovare il parametro 'keep_files'");
			exit(EXIT_FAILURE);
		}

		// Vengono impostate le variabili per l'esecuzione dei parametri di valutazione, la verbosità dei log e i file temporanei
		int do_cov = 0, do_div = 0, do_rep = 0, do_ver = 0, do_keep = 0;
		if (!strcasecmp(coverage, "true") || !strcasecmp(coverage, "yes") || !strcasecmp(coverage, "y") || !strcasecmp(coverage, "s") || !strcmp(coverage, "1") || !strcasecmp(coverage, "ok"))
			do_cov = 1;
		if (!strcasecmp(diversity, "true") || !strcasecmp(diversity, "yes") || !strcasecmp(diversity, "y") || !strcasecmp(diversity, "s") || !strcmp(diversity, "1") || !strcasecmp(diversity, "ok"))
			do_div = 1;
		if (!strcasecmp(representativeness, "true") || !strcasecmp(representativeness, "yes") || !strcasecmp(representativeness, "y") || !strcasecmp(representativeness, "s") || !strcasecmp(representativeness, "1") || !strcmp(representativeness, "ok"))
			do_rep = 1;
		if (!strcasecmp(verbose, "true") || !strcasecmp(verbose, "yes") || !strcasecmp(verbose, "y") || !strcasecmp(verbose, "s") || !strcmp(verbose, "1") || !strcasecmp(verbose, "ok"))
			do_ver = 1;
		if (!strcasecmp(keep_files, "true") || !strcasecmp(keep_files, "yes") || !strcasecmp(keep_files, "y") || !strcasecmp(keep_files, "s") || !strcasecmp(keep_files, "1") || !strcmp(keep_files, "ok"))
			do_keep = 1;

		// Per ogni valore di iterations e per ogni valore di beam viene lanciato lo script autodue.sh
		// Viene programmato il lancio della fork che permette la creazione di processi figli

		// Vengono ordinati gli array delle iterazioni e dei beam
		qsort(iterations, niterations, sizeof(int), compare);
		qsort(beams, nbeams, sizeof(int), compare);

		for(current_beam = 0; current_beam < nbeams; current_beam++)
		{
			// Questa variabile servira' a decidere se il valore di iterazioni corrente e' sensato
			// (ovvero minore o uguale al numero di iterazioni massime per il valore corrente di beam)
			int *max_iterations;

			// Viene inizializzata la memoria condivisa che i vari threads condivideranno
			// per leggere e modificare la variabile appena dichiarata
			int smithers_id;

			smithers_id = shmget((key_t)4321+(current_exp*nbeams+current_beam)*sizeof(int), sizeof(int), 0666 | IPC_CREAT);
			if (smithers_id < 0)
			{
				fprintf(stderr, ERROR_STRING, "Impossibile creare memoria condivisa");
				exit(EXIT_FAILURE);
			}
			int *smithers_addr = (int *)shmat(smithers_id, (void *)0, 0);
			if (smithers_addr == (int *)-1)
			{
				fprintf(stderr, ERROR_STRING, "Indirizzo di memoria condivisa non valido");
				exit(EXIT_FAILURE);
			}
			max_iterations = smithers_addr;

			// L' impostazione di default e' eseguire tutti gli esperimenti
			*max_iterations = -1;

			// Vengono scorse le iterazioni scelte ed eventualmente lanciate le esecuzioni di subdue
			for(current_iter = 0; current_iter < niterations; current_iter++)
			{
				// Se e' stato scelto di eseguire un esperimento con iterations 0
				// questo notifica che sta calcolando il massimo numero di iterazioni
				if (!iterations[current_iter])
					*max_iterations = 0;

				// Viene creata la pipe su 2 file descriptors per effettuare il redirect dell'output dei processi "figlio"
				int fd[2];
				if (pipe(fd) != 0)
				{
					fprintf(stderr, "\n\nERRORE: Impossibile creare pipe\n\n");
					exit(EXIT_FAILURE);
				}

				// Finche' il numero di processi "figlio" in esecuzione e' maggiore del numero
				// di threads consentiti non ne vengono creati altri
				while(*nchildren >= nthreads)
					wait(&status);

				// Viene eseguita la fork per creare un processo figlio
				(*nchildren)++;
				pid_t PID = fork();

				// Blocco di istruzioni eseguite dal figlio
				// All'interno del processo figlio viene lanciato subdue e calcolati i parametri
				// attraverso la combinazione di una fork() ed una una system()
				if(PID == 0)
				{
					// Redirect dell'output del figlio
					if(dup2(fd[1], stdout->_fileno) ==-1)
					{
						fprintf(stderr, "\n\nERRORE: impossibile redirigere standard output");
						exit(EXIT_FAILURE);
					}
					close(fd[0]);
					close(fd[1]);

					// In questo vettore verra' memorizzata la stringa con i comandi da passare alla system
					char system_string[BUFF_SIZE];

					// Se si sta usando iterations 0 viene lanciata una esecuzione di subdue per contare
					// Il numero massimo di iterazioni plausibili. Servirà a permettere di calcolare la
					// copertura in ogni occasione o di evitare esecuzioni inutili
					if (iterations[current_iter] == 0)
					{
						sprintf(system_string, "%s/subdue %s -iterations 0 -beam %d %s | grep -ci iteration",
								subdue_bin_path, parameters, beams[current_beam], fileg_path);
						FILE *read_iter;
						if (!(read_iter = (FILE *)popen(system_string, "r")))
						{
							fprintf(stderr, ERROR_STRING, "Impossibile eseguire popen()");
							exit(EXIT_FAILURE);
						}
						fgets(buffer, sizeof(buffer), read_iter);
						fclose(read_iter);
						*max_iterations = atoi(buffer)/2-1;
					}

					// Se un altro processo sta calcolando il massimo numero di iterazioni plausibili
					// oppure è previsto che venga calcolato (nel caso di nthreads > 1) ...
					if (*max_iterations == 0 || (*max_iterations == -1 && iterations[0] == 0 && iterations[current_iter] != 0))
					{
						// ... il processo corrente si mette in attesa di tale dato finche':
						// 1) non sara' reso disponibile il massimo numero di iterazioni
						// 2) non si avrà un numero di processi figli inferiore a quello desiderato
						(*nchildren)--;
						while (*max_iterations < 1 || *nchildren >= nthreads)
							sleep(1);
						(*nchildren)++;
					}

					// L'esperimento viene eseguito soltanto se effettivamente necessario
					if (iterations[current_iter] < *max_iterations || *max_iterations == -1)
					{
						// Viene creata la stringa contenente il nome del file .subs che sarà usato come output di subdue
						char filename_subs[BUFF_SIZE];
						sprintf(filename_subs, filename_g);
						int lastdot = strrchr(filename_subs, '.') - filename_subs;
						sprintf(filename_subs, "%s", filename_g);
						filename_subs[lastdot+1] = 's';
						filename_subs[lastdot+2] = 'u';
						filename_subs[lastdot+3] = 'b';
						filename_subs[lastdot+4] = 's';
						filename_subs[lastdot+5] = '\0';

						// Vengono create le cartelle necessarie all'esperimento corrente
						sprintf(system_string, "mkdir -p %s/%05d/{log,temp}", prefix, current_dir);
						system(system_string);

						// Viene spostato il file .g nella cartella dell'esperimento
						// in modo che anche il file .cmp verrà creato in questa posizione
						sprintf(system_string, "cp %s/%s %s/%05d", prefix, filename_g, prefix, current_dir);
						system(system_string);

						// Se richiesto viene creato il file immagine contenente il grafo di input nel formato specificato
						if (img_ext)
						{
							sprintf(system_string, "if [[ ! ( -e %s/%s.dot ) ]]; then %s/graph2dot %s/%s %s/%s.dot; fi;",
									prefix, filename_g, subdue_bin_path, prefix, filename_g, prefix, filename_g);
							system(system_string);
							sprintf(system_string, "if [[ ! ( -e %s/%s.dot.%s ) ]]; then dot -O -T%s %s/%s.dot; fi;",
									prefix, filename_g, img_ext, img_ext, prefix, filename_g);
							system(system_string);
						}

						// Viene effettivamente lanciata l'esecuzione di subdue
						sprintf(system_string, "%s/subdue %s -iterations %d -beam %d %s -out %s/%05d/%s %s/%05d/%s >& %s/%05d/log/subdue.log",
								subdue_bin_path,
								parameters,
								(iterations[current_iter])?iterations[current_iter]:*max_iterations,
								beams[current_beam],
								(do_cov)?"-compress":"",
								prefix, current_dir, filename_subs,
								prefix, current_dir, filename_g,
								prefix, current_dir
						);
						system(system_string);

						// Se richiesto viene creato il file immagine contenente la gerarchia delle sottostrutture nel formato specificato
						if (img_ext)
						{
							sprintf(system_string, "if [[ ! ( -e %s/%05d/%s.dot ) ]]; then %s/subs2dot %s/%05d/%s %s/%05d/%s.dot; fi;",
									prefix, current_dir, filename_subs, subdue_bin_path, prefix, current_dir, filename_subs, prefix, current_dir, filename_subs);
							system(system_string);
							sprintf(system_string, "if [[ ! ( -e %s/%05d/%s.dot.%s ) ]]; then dot -O -T%s %s/%05d/%s.dot; fi;",
									prefix, current_dir, filename_subs, img_ext, img_ext, prefix, current_dir, filename_subs);
							system(system_string);
						}

						// Se richiesta viene valutata la copertura (se è stato creato il file .cmp)
						if (do_cov)
						{
							sprintf(system_string, "if [ -e %s/%05d/%s.cmp ]; then %s/coverage %s %s/%05d/%s %s/%05d/%s.cmp >& %s/%05d/log/coverage.log; fi;",
									prefix, current_dir, filename_g,
									subdue_bin_path,
									(do_ver)?("-v"):(""),
									prefix, current_dir, filename_g,
									prefix, current_dir, filename_g,
									prefix, current_dir
							);
							system(system_string);
						}

						// Se richiesta viene valutata la diversita'
						if (do_div)
						{
							sprintf(system_string, "%s/diversity %s %s/%05d/%s >& %s/%05d/log/diversity.log",
									subdue_bin_path,
									(do_ver)?("-v"):(""),
									prefix, current_dir, filename_subs,
									prefix, current_dir
							);
							system(system_string);
						}

						// Se richiesta viene valutata la rappresentativita'
						if (do_rep)
						{
							sprintf(system_string, "%s/representativeness %s %s/%05d/%s %s/%05d/%s >& %s/%05d/log/representativeness.log",
									subdue_bin_path,
									(do_ver)?("-v"):(""),
									prefix, current_dir, filename_g,
									prefix, current_dir, filename_subs,
									prefix, current_dir
							);
							system(system_string);
						}

						// Se non specificato diversamente, vengono eliminati i files temporanei
						if (!do_keep)
						{
							sprintf(system_string, "rm -Rf %s/%05d/temp %s/%05d/%s",
									prefix, current_dir,
									prefix, current_dir, filename_g
							);
							system(system_string);
						}

						// Vengono create le stringhe che conterranno i dati da scrivere nel file di log
						char cov_log[BUFF_SIZE];
						char div_log[BUFF_SIZE];
						char rep_log[BUFF_SIZE];
						FILE *read_log;

						// Viene eseguita una popen per estrarre il valore della copertura e memorizzarlo in una variabile
						sprintf(system_string, "if [ -e %s/%05d/%s.cmp ]; then echo `grep \"La copertura e\" %s/%05d/log/coverage.log | cut -d ' ' -f5`; else echo -1; fi",
								prefix, current_dir, filename_g,
								prefix, current_dir
						);
						if ((read_log = popen(system_string, "r")))
						{
							fgets(cov_log, sizeof(cov_log), read_log);
							fclose(read_log);
							cov_log[strlen(cov_log)-1] = '\0';
						}
						else
						{
							fprintf(stderr, ERROR_STRING, "Impossibile creare il file autodue.log (Copertura non trovata)");
							exit(EXIT_FAILURE);
						}

						// Viene eseguita una popen per estrarre il valore della diversita' e memorizzarlo in una variabile
						sprintf(system_string, "grep \"La diversita' vale\" %s/%05d/log/diversity.log | cut -d ' ' -f4",
								prefix, current_dir
						);
						if ((read_log = popen(system_string, "r")))
						{
							fgets(div_log, sizeof(div_log), read_log);
							fclose(read_log);
							div_log[strlen(div_log)-1] = '\0';
						}
						else
						{
							fprintf(stderr, ERROR_STRING, "Impossibile creare il file autodue.log (Diversita' non trovata)");
							exit(EXIT_FAILURE);
						}

						// Viene eseguita una popen per estrarre il valore della rappresentativita'' e memorizzarlo in una variabile
						sprintf(system_string, "REP_STRING=`grep \"La rappresentativita\" %s/%05d/log/representativeness.log | cut -d ' ' -f9`; TOPSUB=0; NTOPSUB=`grep -c \"La rappresentativita\" %s/%05d/log/representativeness.log`; while [ $TOPSUB -lt $NTOPSUB ]; do ((TOPSUB++)); REP_LOG=\"$REP_LOG,`echo $REP_STRING | cut -d ' ' -f$TOPSUB`\"; done; echo $REP_LOG",
								prefix, current_dir,
								prefix, current_dir
						);
						if ((read_log = popen(system_string, "r")))
						{
							fgets(rep_log, sizeof(rep_log), read_log);
							fclose(read_log);
						}
						else
						{
							fprintf(stderr, ERROR_STRING, "Impossibile creare il file autodue.log (Rappresentativita' non trovata)");
							exit(EXIT_FAILURE);
						}

						// Viene scritta sul file di log la riga contenente le informazioni per l'esecuzione corrente
						sem_wait(sem_log);
						sprintf(log_buffer, "%05d,%d,%d,%d,%s,%s%s",
								current_dir, iterations[current_iter], *max_iterations, beams[current_beam], (do_cov)?cov_log:"-1", (do_div)?div_log:"-1", (do_rep)?rep_log:",-1");

						if ((autodue_log = fopen(filename_log, "a")))
						{
							fputs(log_buffer, autodue_log);
							fclose(autodue_log);
						}
						else
						{
							fprintf(stderr, ERROR_STRING, "Impossibile aprire il file autodue.log");
						}
						sem_post(sem_log);

						// Prima di terminare il processo figlio viene decrementato
						// il numero di processi figli ancora in esecuzione
						(*nchildren)--;
						return EXIT_SUCCESS;
					}

					// Viene scritta su file la riga contennte le informazioni dell'esecuzione non lanciata
					sem_wait(sem_log);
					sprintf(log_buffer, "%05d,%d,%d,%d\n", current_dir, iterations[current_iter], *max_iterations, beams[current_beam]);

					if ((autodue_log = fopen(filename_log, "a")))
					{
						fputs(log_buffer, autodue_log);
						fclose(autodue_log);
					}
					else
					{
						fprintf(stderr, ERROR_STRING, "Impossibile aprire il file autodue.log");
					}
					sem_post(sem_log);

					// Prima di terminare il processo figlio viene decrementato
					// il numero di processi figli ancora in esecuzione
					(*nchildren)--;
					return EXIT_FAILURE;
				}
				// Blocco di istruzioni eseguite dal padre
				else if (PID > 0)
				{
					printf("    Processo %6d avviato (DIR:%5d, Beam:%3d, Iterations%5d)\n", PID, current_dir, beams[current_beam], iterations[current_iter]);
					close(fd[1]);
				}
				else
				{
					fprintf(stderr, "\n\nERRORE: Impossibile eseguire fork()\n\n");
					exit(EXIT_FAILURE);
				}
				current_dir++;
			}
		}
	}

	xmlFreeDoc(doc_xml);

	xmlCleanupParser();

	printf("\nTutti gli esperimenti sono stati lanciati\n");
	printf("In attesa che terminino i processi ancora in esecuzione\n");

	while (*nchildren)
		printf("    Processo %d terminato\n", wait(&status));

	printf("\nEsecuzione terminata\n");

	return 0;
}
